package cn.john.user.service.impl;

import cn.john.basic.exception.BusinessException;
import cn.john.basic.util.AjaxResult;
import cn.john.basic.util.HttpClientUtils;
import cn.john.basic.util.MD5Utils;
import cn.john.basic.util.StrUtils;
import cn.john.user.constants.UserConstants;
import cn.john.user.domain.LoginInfo;
import cn.john.user.domain.User;
import cn.john.user.domain.WxUser;
import cn.john.user.dto.LoginDto;
import cn.john.user.mapper.LoginInfoMapper;
import cn.john.user.mapper.UserMapper;
import cn.john.user.mapper.WxUserMapper;
import cn.john.user.service.ILoginService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LoginInfoMapper loginInfoMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    private UserMapper userMapper;

    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        Map<String,Object> map = new HashMap<>();
        // 1. 校验
        // 1.1 空校验
        if(StringUtils.isEmpty(loginDto.getUsername())
                || StringUtils.isEmpty(loginDto.getPassword())
                || StringUtils.isEmpty(loginDto.getLoginType())) {
            throw new BusinessException("请填写完整信息!");
        }
        // 1.2 校验用户是否存在
        LoginInfo loginInfo = loginInfoMapper.loadByLoginDto(loginDto);
        if(loginInfo == null) {
            throw new BusinessException("用户不存在!");
        }
        // 1.3 校验密码是否正确
        String salt = loginInfo.getSalt();
        String md5Pwd = MD5Utils.encrypByMd5(loginDto.getPassword() + salt);
        if (!md5Pwd.equals(loginInfo.getPassword())) {
            throw new BusinessException("用户名或密码错误!");
        }
        // 2. 存储登录成功信息,设置token30分钟过期
        String token = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
        // 3. 返回登录信息
        map.put("token", token);
        // 注意：类似密码等敏感字段不能返回给前端
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        map.put("loginInfo", loginInfo);
        return map;
    }

    @Override
    public AjaxResult wechatLogin(String code) {
        // 1.校验
        if (StringUtils.isEmpty(code)) {
            throw new BusinessException("code不能为空!");
        }
        // 2.通过code从微信中拿token
        String getTokenUrl = UserConstants.GET_TOKEN_URL
                .replace("APPID", UserConstants.APPID)
                .replace("SECRET", UserConstants.SECRET)
                .replace("CODE", code);
        String tokenStr = HttpClientUtils.httpGet(getTokenUrl);
        // 使用fastjson组件将字符串转换为对象
        JSONObject tokenObj = JSONObject.parseObject(tokenStr);
        Object accessToken = tokenObj.getString("access_token");
        String openId = tokenObj.getString("openid");
        // 3.获取token后，从本地数据库中查询是否已经有微信用户并且查询是否已经绑定了平台用户
        WxUser wxUser = wxUserMapper.loadByOpenId(openId);
        if (wxUser != null && wxUser.getUser_id() != null) {
            // 有用户而且已经绑定了 则直接免密登录
            LoginInfo loginInfo = loginInfoMapper.loadByUserId(wxUser.getUser_id());
            // 存储登录成功信息
            Map<String,Object> map = new HashMap<>();
            String token = UUID.randomUUID().toString();
            /* 存储的时候设置30分钟过期 */
            redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
            // 返回登录成功信息
            map.put("token", token);
            // 针对一些敏感字段，不应该返回前端
            loginInfo.setSalt(null);
            loginInfo.setPassword(null);
            map.put("loginInfo", loginInfo);
            map.put("nickname", wxUser.getNickname());
            return AjaxResult.me().setResultObj(map);
        }
        // 如果没有，就返回前端，跳转绑定页面
        String binderUrlParam = "?accessToken=" + accessToken + "&openId=" + openId;
        return AjaxResult.me().setSuccess(false).setResultObj(binderUrlParam);
    }

    @Override
    public AjaxResult binderWechat(Map<String, String> param) {
        String phone = param.get("phone");
        String verifyCode = param.get("verifyCode");
        String accessToken = param.get("accessToken");
        String openId = param.get("openId");
        // 校验手机号或验证码不为空
        /*if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)) {
            throw new BusinessException("手机号或验证码不能为空!");
        }*/
        // 判断验证码是否有效
        Object objTmp = redisTemplate.opsForValue().get(UserConstants.BINDER_PHONE_PREFIX + phone);
        // 如果从redis中拿不到验证码，则验证码失效或者没有获取
        if (objTmp == null) {
            throw new BusinessException("验证码已失效，请重新获取!");
        }
        // 从redis中拿到了验证码，比对是否一致
        String code = ((String)objTmp).split(":")[0];
        // 如果不一致则 验证码填写错误
        if (!verifyCode.equalsIgnoreCase(code)) {
            throw new BusinessException("验证码错误!");
        }
        // 判断手机号是否已经注册
        User userTmp = userMapper.loadByPhone(phone);
        User user = null;
        LoginInfo loginInfo = null;
        // 如果没有注册，则得到一个平台用户，注册t_user 和 t_loginInfo
        if(userTmp == null) {
            user = initUser(phone);
            loginInfo = user2LoginInfo(user);
            loginInfoMapper.save(loginInfo);
            user.setLoginInfo(loginInfo);
            userMapper.save(user);
        }else {
            // 已经注册过了，则查询出用户
            user = userTmp;
            loginInfo = loginInfoMapper.loadByUserId(userTmp.getId());
        }
        // 绑定用户和微信
        // 从微信平台查询出微信用户信息，生成t_wxuser
        String getWxUserURL = UserConstants.GET_WXUSER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId);
        String userStr = HttpClientUtils.httpGet(getWxUserURL);
        // 绑定t_wxuser 和 t_user
        WxUser wxUser = initAndBinderWxUser(userStr,user);
        wxUserMapper.save(wxUser);
        // 免密登录,有用户且绑定了直接免密登录存储成功信息
        Map<String,Object> map = new HashMap<>();
        String token = UUID.randomUUID().toString();
        /* 存储的时候设置30分钟过期 */
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
        // 返回登录成功信息
        map.put("token", token);
        // 敏感字段不返回给前端
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        map.put("nickname", wxUser.getNickname());
        map.put("loginInfo", loginInfo);
        return AjaxResult.me().setResultObj(map);
    }

    private WxUser initAndBinderWxUser(String userStr, User user) {
        // 转换微信用户信息
        JSONObject jsonObject = JSONObject.parseObject(userStr);
        WxUser wxUser = new WxUser();
        wxUser.setUser_id(user.getId());
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(null);
        wxUser.setHeadimgurl(jsonObject.getString("headimgrul"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }

    private LoginInfo user2LoginInfo(User user) {
        LoginInfo loginInfo = new LoginInfo();
        BeanUtils.copyProperties(user, loginInfo);
        // 1 前端用户
        loginInfo.setType(1);
        return loginInfo;
    }

    private User initUser(String phone) {
        // 1.定义返回值对象
        User user = new User();
        // 给对象设置值
        user.setPhone(phone);
        user.setUsername(phone);
        // 设置盐值和密码
        String salt = StrUtils.getComplexRandomString(32);
        // 设置一个6位随机密码
        String md5Pwd = MD5Utils.encrypByMd5(StrUtils.getComplexRandomString(6) + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        // 返回对象
        return user;
    }


}
